package com.iweb.controller;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.iweb.annotation.TokenAuthentication;
import com.iweb.common.page.DataGrid;
import com.iweb.common.result.EmptyObject;
import com.iweb.common.result.ResultEntity;
import com.iweb.model.Article;
import com.iweb.model.Member;
import com.iweb.model.Notice;
import com.iweb.service.ArticleService;
import com.iweb.service.MemberService;
import com.iweb.service.NoticeService;
import io.swagger.annotations.ApiOperation;
import lombok.SneakyThrows;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.DirectExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.rabbit.core.RabbitAdmin;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Set;

/**
 * <p>
 * 前端控制器
 * </p>
 *
 * @author jack
 * @since 2020-05-15
 */
@RestController
@RequestMapping("/article")
public class ArticleController {
    @Autowired
    private ArticleService articleService;

    @Autowired
    private MemberService memberService;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Autowired
    private NoticeService noticeService;

    @Cacheable(value = "articles", key = "#root.method.name+#current")
    @ApiOperation("分页查询所有文章")
    @GetMapping("{current}/{size}")
    public ResultEntity<DataGrid<Article>> findAllArticle(@PathVariable Integer current,
                                                          @PathVariable Integer size) {
        DataGrid<Article> dataGrid = new DataGrid<>();
        IPage<Article> page = new Page<>(current, size);
        QueryWrapper<Article> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("is_public", "1").orderByDesc("create_time");
        IPage<Article> articleIPage = articleService.page(page, queryWrapper);
        // 查询作者
        List<Article> articles = articleIPage.getRecords();
        for (Article article : articles) {
            Member member = memberService.getById(article.getUserId());
            article.setMember(member);
        }
        dataGrid.setRows(articles);
        dataGrid.setTotal(articleIPage.getTotal());
        return ResultEntity.SUCCESS(dataGrid);
    }

    @TokenAuthentication
    @ApiOperation("添加文章")
    @PostMapping
    public ResultEntity<Article> addArticle(@RequestBody Article article) {
        // 从token中获取userId
        HttpServletRequest request =
                ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest();
        String authorId = String.valueOf(request.getAttribute("userId"));
        article.setUserId(authorId);
        // 文章主键由分布式ID生成器自动生成
        // 设定文章创建时间
        article.setCreateTime(LocalDateTime.now());
        article.setUpdateTime(LocalDateTime.now());
        // 设定文章是否公开
        article.setIsPublic("1"); // 0 不公开 1 公开
        // 设定文章是否置顶
        article.setIsTop("0");// 0 不置顶 1 置顶
        // 设定文章访问量
        article.setVisits(0);
        // 设定文章点赞数
        article.setThumbUp(0);
        // 设定文章评论数
        article.setCommentCount(0);
        articleService.save(article);

        // 获取订阅者
        String authorKey = "subscribe_" + authorId;
        Set<String> users = redisTemplate.boundSetOps(authorKey).members();
        if (users != null) {
            for (String userId : users) {
                // 封装通知
                Notice notice = new Notice();
                notice.setAction("add"); // add 表示新增文章
                notice.setOperatorId(authorId);
                notice.setReceiverId(userId);
                notice.setTargetType("article");
                notice.setTargetId(article.getId());
                notice.setType("sys");
                notice.setCreateTime(LocalDateTime.now());
                notice.setState("0");
                // 保存通知
                noticeService.save(notice);
            }
            // 发送通知到队列
            rabbitTemplate.convertAndSend("article_subscribe", authorId, article.getId());
        }
        return ResultEntity.SUCCESS(article);
    }

    @TokenAuthentication
    @ApiOperation("根据文章id修改文章")
    @PutMapping("{articleId}")
    public ResultEntity<Article> updateArticle(@PathVariable String articleId,
                                               @RequestBody Article article) {
        // 设定修改的文章ID
        article.setId(articleId);
        // 修改文章,根据文章ID 修改文章
        articleService.updateById(article);
        // 查询修改后的文章
        Article articleResult = articleService.getById(articleId);
        return ResultEntity.SUCCESS(articleResult);
    }

    @TokenAuthentication
    @ApiOperation("删除文章")
    @DeleteMapping("{articleId}")
    public ResultEntity<EmptyObject> deleteArticleById(@PathVariable String articleId) {
        boolean flag = articleService.removeById(articleId);
        // 如果true 则返回成功
        // 否则返回失败
        if (flag) {
            return ResultEntity.SUCCESS(); // 自动封装了一个返回的空对象
        } else {
            return ResultEntity.FAIL(); // 自动封装了一个返回的空对象
        }
    }

    @ApiOperation("根据标题模糊分页查询")
    @PostMapping("findAllByQueryPage/{current}/{size}")
    public ResultEntity<DataGrid<Article>> findAllByQueryPage(@PathVariable Integer current,
                                                              @PathVariable Integer size,
                                                              @RequestBody Article article) {
        DataGrid<Article> dataGrid = new DataGrid<>();
        // 封装分页条件
        IPage<Article> page = new Page<>(current, size);
        // 封装条件查询器
        QueryWrapper<Article> queryWrapper = new QueryWrapper<>();
        queryWrapper.like("title", article.getTitle());
        queryWrapper.eq("is_public", "1");
        IPage<Article> articleIPage = articleService.page(page, queryWrapper);
        dataGrid.setRows(articleIPage.getRecords());
        dataGrid.setTotal(articleIPage.getTotal());
        return ResultEntity.SUCCESS(dataGrid);
    }

    @SneakyThrows
    @TokenAuthentication // 需要token认证
    @ApiOperation("根据文章订阅作者")
    @PostMapping("/subscribe/{articleId}")
    public ResultEntity<String> subscribe(@PathVariable String articleId) {
        // 根据文章id查询 作者id
        String authorId = articleService.getById(articleId).getUserId();
        // 从token中获取userId
        HttpServletRequest request =
                ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest();
        String userId = String.valueOf(request.getAttribute("userId"));
        // 到redis查询是否已经订阅
        // redis 以 set 的方式保存订阅关系
        // 作者被订阅：key=subscribe_authorId , values = [userId...]
        // 用户订阅作者：key=subscribe_userId , values = [authorId...]
        String authorKey = String.format("subscribe_%s", authorId);
        String userKey = String.format("subscribe_%s", userId);

        RabbitAdmin rabbitAdmin = new RabbitAdmin(rabbitTemplate);
        // 声明 文章订阅交换机 article_subscribe
        DirectExchange exchange = new DirectExchange("article_subscribe");
        rabbitAdmin.declareExchange(exchange);
        // 声明 队列
        Queue queue = new Queue("article_subscribe_" + userId, true);
        rabbitAdmin.declareQueue(queue);
        // 将队列通过路由键绑定或解绑direct交换机  routing key = authorId
        Binding binding = BindingBuilder.bind(queue).to(exchange).with(authorId);

        Boolean flag = redisTemplate.boundSetOps(authorKey).isMember(userId);
        if (flag) {
            // 如果订阅则取消订阅
            redisTemplate.boundSetOps(authorKey).remove(userId);
            redisTemplate.boundSetOps(userKey).remove(authorId);
            // 取消绑定队列到交换机
            rabbitAdmin.removeBinding(binding);
            return ResultEntity.SUCCESS("取消订阅成功");
        } else {
            // 如果未订阅则进行订阅并保存redis
            redisTemplate.boundSetOps(authorKey).add(userId);
            redisTemplate.boundSetOps(userKey).add(authorId);
            // 绑定队列到交换机
            rabbitAdmin.declareBinding(binding);
            return ResultEntity.SUCCESS("订阅成功");
        }
    }

    @ApiOperation("根据id查询文章")
    @GetMapping("{articleId}")
    public ResultEntity<Article> findArticleById(@PathVariable String articleId) {
        Article article = articleService.getById(articleId);
        return ResultEntity.SUCCESS(article);
    }
}

